﻿using System;
using GeoAPI.Geometries;
using YJKGridTopologyCal.Geometries;

namespace YJKGridTopologyCal.LinearReferencing
{
	public class LinearLocation : IComparable<LinearLocation>, IComparable, ICloneable
	{
		public static LinearLocation GetEndLocation(IGeometry linear)
		{
			if (!(linear is ILineString) && !(linear is IMultiLineString))
			{
				throw new ArgumentException(string.Format("Expected {0} or {1}, but was {2}", typeof(ILineString), typeof(IMultiLineString), linear.GetType()), "linear");
			}
			LinearLocation linearLocation = new LinearLocation();
			linearLocation.SetToEnd(linear);
			return linearLocation;
		}

		public static Coordinate PointAlongSegmentByFraction(Coordinate p0, Coordinate p1, double fraction)
		{
			if (fraction <= 0.0)
			{
				return p0;
			}
			if (fraction >= 1.0)
			{
				return p1;
			}
			double x = (p1.X - p0.X) * fraction + p0.X;
			double y = (p1.Y - p0.Y) * fraction + p0.Y;
			double z = (p1.Z - p0.Z) * fraction + p0.Z;
			return new Coordinate(x, y, z);
		}

		public LinearLocation()
		{
		}

		public LinearLocation(int segmentIndex, double segmentFraction) : this(0, segmentIndex, segmentFraction)
		{
		}

		public LinearLocation(int componentIndex, int segmentIndex, double segmentFraction) : this(componentIndex, segmentIndex, segmentFraction, true)
		{
		}

		private LinearLocation(int componentIndex, int segmentIndex, double segmentFraction, bool normalize)
		{
			this._componentIndex = componentIndex;
			this._segmentIndex = segmentIndex;
			this._segmentFraction = segmentFraction;
			if (normalize)
			{
				this.Normalize();
			}
		}

		public LinearLocation(LinearLocation loc)
		{
			this._componentIndex = loc._componentIndex;
			this._segmentIndex = loc._segmentIndex;
			this._segmentFraction = loc._segmentFraction;
		}

		private void Normalize()
		{
			if (this._segmentFraction < 0.0)
			{
				this._segmentFraction = 0.0;
			}
			if (this._segmentFraction > 1.0)
			{
				this._segmentFraction = 1.0;
			}
			if (this._componentIndex < 0)
			{
				this._componentIndex = 0;
				this._segmentIndex = 0;
				this._segmentFraction = 0.0;
			}
			if (this._segmentIndex < 0)
			{
				this._segmentIndex = 0;
				this._segmentFraction = 0.0;
			}
			if (this._segmentFraction == 1.0)
			{
				this._segmentFraction = 0.0;
				this._segmentIndex++;
			}
		}

		public void Clamp(IGeometry linear)
		{
			if (this._componentIndex >= linear.NumGeometries)
			{
				this.SetToEnd(linear);
				return;
			}
			if (this._segmentIndex >= linear.NumPoints)
			{
				ILineString lineString = (ILineString)linear.GetGeometryN(this._componentIndex);
				this._segmentIndex = lineString.NumPoints - 1;
				this._segmentFraction = 1.0;
			}
		}

		public void SnapToVertex(IGeometry linearGeom, double minDistance)
		{
			if (this._segmentFraction <= 0.0 || this._segmentFraction >= 1.0)
			{
				return;
			}
			double segmentLength = this.GetSegmentLength(linearGeom);
			double num = this._segmentFraction * segmentLength;
			double num2 = segmentLength - num;
			if (num <= num2 && num < minDistance)
			{
				this._segmentFraction = 0.0;
				return;
			}
			if (num2 <= num && num2 < minDistance)
			{
				this._segmentFraction = 1.0;
			}
		}

		public double GetSegmentLength(IGeometry linearGeom)
		{
			ILineString lineString = (ILineString)linearGeom.GetGeometryN(this._componentIndex);
			int num = this._segmentIndex;
			if (this._segmentIndex >= lineString.NumPoints - 1)
			{
				num = lineString.NumPoints - 2;
			}
			Coordinate coordinateN = lineString.GetCoordinateN(num);
			Coordinate coordinateN2 = lineString.GetCoordinateN(num + 1);
			return coordinateN.Distance(coordinateN2);
		}

		public void SetToEnd(IGeometry linear)
		{
			this._componentIndex = linear.NumGeometries - 1;
			ILineString lineString = (ILineString)linear.GetGeometryN(this._componentIndex);
			this._segmentIndex = lineString.NumPoints - 1;
			this._segmentFraction = 1.0;
		}

		public int ComponentIndex
		{
			get
			{
				return this._componentIndex;
			}
		}

		public int SegmentIndex
		{
			get
			{
				return this._segmentIndex;
			}
		}

		public double SegmentFraction
		{
			get
			{
				return this._segmentFraction;
			}
		}

		public bool IsVertex
		{
			get
			{
				return this._segmentFraction <= 0.0 || this._segmentFraction >= 1.0;
			}
		}

		public Coordinate GetCoordinate(IGeometry linearGeom)
		{
			ILineString lineString = (ILineString)linearGeom.GetGeometryN(this._componentIndex);
			Coordinate coordinateN = lineString.GetCoordinateN(this._segmentIndex);
			if (this._segmentIndex >= lineString.NumPoints - 1)
			{
				return coordinateN;
			}
			Coordinate coordinateN2 = lineString.GetCoordinateN(this._segmentIndex + 1);
			return LinearLocation.PointAlongSegmentByFraction(coordinateN, coordinateN2, this._segmentFraction);
		}

		public LineSegment GetSegment(IGeometry linearGeom)
		{
			ILineString lineString = (ILineString)linearGeom.GetGeometryN(this._componentIndex);
			Coordinate coordinateN = lineString.GetCoordinateN(this._segmentIndex);
			if (this._segmentIndex >= lineString.NumPoints - 1)
			{
				return new LineSegment(lineString.GetCoordinateN(lineString.NumPoints - 2), coordinateN);
			}
			Coordinate coordinateN2 = lineString.GetCoordinateN(this._segmentIndex + 1);
			return new LineSegment(coordinateN, coordinateN2);
		}

		public bool IsValid(IGeometry linearGeom)
		{
			if (this._componentIndex < 0 || this._componentIndex >= linearGeom.NumGeometries)
			{
				return false;
			}
			ILineString lineString = (ILineString)linearGeom.GetGeometryN(this._componentIndex);
			return this._segmentIndex >= 0 && this._segmentIndex <= lineString.NumPoints && (this._segmentIndex != lineString.NumPoints || this._segmentFraction == 0.0) && this._segmentFraction >= 0.0 && this._segmentFraction <= 1.0;
		}

		public int CompareTo(object obj)
		{
			LinearLocation other = (LinearLocation)obj;
			return this.CompareTo(other);
		}

		public int CompareTo(LinearLocation other)
		{
			if (this._componentIndex < other.ComponentIndex)
			{
				return -1;
			}
			if (this._componentIndex > other.ComponentIndex)
			{
				return 1;
			}
			if (this._segmentIndex < other.SegmentIndex)
			{
				return -1;
			}
			if (this._segmentIndex > other.SegmentIndex)
			{
				return 1;
			}
			if (double.IsNaN(this._segmentFraction) && double.IsNaN(other._segmentFraction))
			{
				return 0;
			}
			if (this._segmentFraction < other.SegmentFraction)
			{
				return -1;
			}
			if (this._segmentFraction > other.SegmentFraction)
			{
				return 1;
			}
			return 0;
		}

		public int CompareLocationValues(int componentIndex1, int segmentIndex1, double segmentFraction1)
		{
			if (this._componentIndex < componentIndex1)
			{
				return -1;
			}
			if (this._componentIndex > componentIndex1)
			{
				return 1;
			}
			if (this._segmentIndex < segmentIndex1)
			{
				return -1;
			}
			if (this._segmentIndex > segmentIndex1)
			{
				return 1;
			}
			if (this._segmentFraction < segmentFraction1)
			{
				return -1;
			}
			if (this._segmentFraction > segmentFraction1)
			{
				return 1;
			}
			return 0;
		}

		public static int CompareLocationValues(int componentIndex0, int segmentIndex0, double segmentFraction0, int componentIndex1, int segmentIndex1, double segmentFraction1)
		{
			if (componentIndex0 < componentIndex1)
			{
				return -1;
			}
			if (componentIndex0 > componentIndex1)
			{
				return 1;
			}
			if (segmentIndex0 < segmentIndex1)
			{
				return -1;
			}
			if (segmentIndex0 > segmentIndex1)
			{
				return 1;
			}
			if (segmentFraction0 < segmentFraction1)
			{
				return -1;
			}
			if (segmentFraction0 > segmentFraction1)
			{
				return 1;
			}
			return 0;
		}

		public bool IsOnSameSegment(LinearLocation loc)
		{
			return this._componentIndex == loc._componentIndex && (this._segmentIndex == loc._segmentIndex || (loc._segmentIndex - this._segmentIndex == 1 && loc._segmentFraction == 0.0) || (this._segmentIndex - loc._segmentIndex == 1 && this._segmentFraction == 0.0));
		}

		public bool IsEndpoint(IGeometry linearGeom)
		{
			int num = ((ILineString)linearGeom.GetGeometryN(this._componentIndex)).NumPoints - 1;
			return this._segmentIndex >= num || (this._segmentIndex == num && this._segmentFraction >= 1.0);
		}

		public LinearLocation ToLowest(IGeometry linearGeom)
		{
			int num = linearGeom.GetGeometryN(this._componentIndex).NumPoints - 1;
			if (this._segmentIndex < num)
			{
				return this;
			}
			return new LinearLocation(this._componentIndex, num, 1.0, false);
		}

		public object Clone()
		{
			return new LinearLocation(this._segmentIndex, this._segmentFraction);
		}

		private int _componentIndex;

		private int _segmentIndex;

		private double _segmentFraction;
	}
}
